﻿using Sidvall.Serialization;
using System.Linq;
using System.Threading.Tasks;

namespace Sidvall.Data
{
    public class SqlManager
    {
        #region Public Members

        public ISqlProvider SqlProvider { get; set; }

        #region CreateDatabaseAsync

        public async Task<bool> CreateDatabaseAsync(string connectionString, string database, SqlExceptionBehavior exceptionBehavior)
        {
            return await this.SqlProvider.CreateDatabaseAsync(connectionString, database, exceptionBehavior).ConfigureAwait(false);
        }

        #endregion
        #region ExecuteNonQueryAsync

        public async Task<int> ExecuteNonQueryAsync(string connectionString, string sqlQuery, SqlExceptionBehavior exceptionBehavior)
        {
            return await this.SqlProvider.ExecuteNonQueryAsync(connectionString, sqlQuery, exceptionBehavior).ConfigureAwait(false);
        }

        #endregion
        #region ExecuteNonQueryAsync

        public async Task<object> ExecuteScalarAsync(string connectionString, string sqlQuery, SqlExceptionBehavior exceptionBehavior)
        {
            return await this.SqlProvider.ExecuteScalarAsync(connectionString, sqlQuery, exceptionBehavior).ConfigureAwait(false);
        }

        #endregion
        #region GetTablesAsync

        public async Task<TableItemCollection> GetTablesAsync(string connectionString, bool loadRowCount, SqlExceptionBehavior exceptionBehavior)
        {
            return await this.SqlProvider.GetTablesAsync(connectionString, loadRowCount, exceptionBehavior).ConfigureAwait(false);
        }

        #endregion

        #region GetSqlComparison

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
        public string GetSqlComparison(string expression, string value, ComparisonOperator comparisonOperator)
        {
            var query = new SqlQuery(this.SqlProvider.GetParameterName(null));
            this.SqlProvider.InitializeSqlComparison(query, expression, value, comparisonOperator, null);
            return query.Sql.ToString();
        }

        #endregion
        #region GetSqlComparisonArray

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
        public string GetSqlComparisonArray(string expression, string value, ComparisonOperator comparisonOperator)
        {
            var sb = new System.Text.StringBuilder();
            InitializeSqlComparisonArray(sb, expression, value, comparisonOperator);
            return sb.ToString();
        }

        #endregion

        #region GetSqlValue

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider", MessageId = "Sidvall.Serialization.ObjectExtension.ToNullableTimeSpan(System.Object)")]
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
        public string GetSqlValue(object value, DataType valueDataType)
        {
            if (value == null)
                return "NULL";
            switch (valueDataType)
            {
                case DataType.Boolean:
                    var booleanValue = value.ToNullableBoolean();
                    if (booleanValue != null)
                        return Sidvall.Serialization.SerializerManager.Current.Serialize(booleanValue.Value);
                    return "NULL";
                case DataType.Byte:
                    var byteValue = value.ToNullableByte();
                    if (byteValue != null)
                        return Sidvall.Serialization.SerializerManager.Current.Serialize(byteValue.Value);
                    return "NULL";
                case DataType.DateTime:
                    var dateTimeValue = value.ToNullableDateTime();
                    if (dateTimeValue != null)
                        return "'" + Sidvall.Serialization.SerializerManager.Current.Serialize(dateTimeValue.Value) + "'";
                    return "NULL";
                case DataType.DateTimeOffset:
                    var dateTimeOffsetValue = value.ToNullableDateTimeOffset();
                    if (dateTimeOffsetValue != null)
                        return "'" + Sidvall.Serialization.SerializerManager.Current.Serialize(dateTimeOffsetValue.Value) + "'";
                    return "NULL";
                case DataType.Decimal:
                    var decimalValue = value.ToNullableDecimal();
                    if (decimalValue != null)
                        return Sidvall.Serialization.SerializerManager.Current.Serialize(decimalValue.Value);
                    return "NULL";
                case DataType.Double:
                    var doubleValue = value.ToNullableDouble();
                    if (doubleValue != null)
                        return Sidvall.Serialization.SerializerManager.Current.Serialize(doubleValue.Value);
                    return "NULL";
                case DataType.Int16:
                    var int16Value = value.ToNullableInt16();
                    if (int16Value != null)
                        return Sidvall.Serialization.SerializerManager.Current.Serialize(int16Value.Value);
                    return "NULL";
                case DataType.Int32:
                    var int32Value = value.ToNullableInt16();
                    if (int32Value != null)
                        return Sidvall.Serialization.SerializerManager.Current.Serialize(int32Value.Value);
                    return "NULL";
                case DataType.Int64:
                    var int64Value = value.ToNullableInt16();
                    if (int64Value != null)
                        return Sidvall.Serialization.SerializerManager.Current.Serialize(int64Value.Value);
                    return "NULL";
                case DataType.Object:
                    return "'" + Sidvall.Serialization.SerializerManager.Current.SerializeObject(value).Replace("'", "''") + "'";
                case DataType.Single:
                    var singleValue = value.ToNullableSingle();
                    if (singleValue != null)
                        return Sidvall.Serialization.SerializerManager.Current.Serialize(singleValue.Value);
                    return "NULL";
                case DataType.TimeSpan:
                    var timeSpanValue = value.ToNullableTimeSpan();
                    if (timeSpanValue != null)
                        return Sidvall.Serialization.SerializerManager.Current.Serialize(timeSpanValue.Value);
                    return "NULL";
                case DataType.Guid:
                    var guidValue = value.ToNullableGuid();
                    if (guidValue != null)
                        return "'" + Sidvall.Serialization.SerializerManager.Current.Serialize(guidValue.Value) + "'";
                    return "NULL";
                case DataType.Char:
                case DataType.String:
                case DataType.Xml:
                    return "'" + value.ToString(string.Empty).Replace("'", "''") + "'";
                case DataType.Binary:
                    var binaryValue = value as byte[];
                    if (binaryValue != null)
                        return Sidvall.Serialization.SerializerManager.Current.Serialize(binaryValue);
                    return "NULL";
                default:
                    throw new System.NotImplementedException(valueDataType.ToString() + " not implemented!");
            }
        }

        #endregion
        #region GetSqlValueArray

        public string GetSqlValueArray(System.Array items, DataType valueDataType, bool ignoreNullValues)
        {
            var sb = new System.Text.StringBuilder();
            InitializeSqlValueArray(sb, items, valueDataType, ignoreNullValues);
            return sb.ToString();
        }

        #endregion

        #region GetSqlWhere

        public string GetSqlWhere(IFilterItem filter)
        {
            System.Text.StringBuilder sb;
            System.Collections.Generic.ICollection<IFilterItem> subFilters;
            string sql;

            if (filter == null)
                return null;
            subFilters = filter.GetSubFilters();
            if (subFilters == null)
            {
                if (filter.ValueIsArray)
                {
                    sql = GetSqlValueArray(filter.Value as System.Array, filter.ValueDataType, true);
                    return GetSqlComparisonArray(filter.FieldName, sql, filter.ComparisonOperator);
                }
                else
                {
                    sql = GetSqlValue(filter.Value, filter.ValueDataType);
                    return GetSqlComparison(filter.FieldName, sql, filter.ComparisonOperator);
                }
            }
            else if (subFilters.Count == 0)
            {
                return null;
            }
            else if (subFilters.Count == 1)
            {
                return GetSqlWhere(subFilters.First());
            }
            else
            {
                sb = new System.Text.StringBuilder();
                foreach (var subFilter in subFilters)
                {
                    sql = GetSqlWhere(subFilter);
                    if (!string.IsNullOrWhiteSpace(sql))
                    {
                        if (sb.Length > 0)
                        {
                            if (filter.LogicalOperator == LogicalOperator.And)
                                sb.Append(" AND ");
                            else
                                sb.Append(" OR ");
                        }
                        sb.Append("(" + sql + ")");
                    }
                }
                return sb.ToString();
            }
        }

        #endregion
        #region GetSqlWhereQuery

        public SqlQuery GetSqlWhereQuery(IFilterItem filter, string tableName)
        {
            return GetSqlWhereQuery(filter, tableName, 0);
        }
        private SqlQuery GetSqlWhereQuery(IFilterItem filter, string tableName, int parameterStartIndex)
        {
            var query = new SqlQuery(this.SqlProvider.GetParameterName(null), parameterStartIndex);
            return GetSqlWhereQuery(filter, tableName, query);
        }
        private SqlQuery GetSqlWhereQuery(IFilterItem filter, string tableName, SqlQuery query)
        {
            InitializeSqlWhereQuery(query, filter, tableName);
            return query;
        }

        #endregion
        #region GetSqlOrderBy

        public string GetSqlOrderBy(global::Sidvall.Data.ISortItem sortItem, string tableName)
        {
            if (sortItem == null)
                return null;
            if (string.IsNullOrWhiteSpace(sortItem.FieldName))
                return null;
            if (sortItem.Ascending)
                return GetFieldName(tableName, sortItem.FieldName);
            else
                return GetFieldName(tableName, sortItem.FieldName) + " desc";
        }
        public string GetSqlOrderBy(System.Collections.Generic.IEnumerable<global::Sidvall.Data.ISortItem> sortItems, string tableName)
        {
            if (sortItems == null)
                return null;
            if (!sortItems.Any())
                return null;

            var sb = new System.Text.StringBuilder();
            foreach (var sortItem in sortItems)
            {
                var item = GetSqlOrderBy(sortItem, tableName);
                if (!string.IsNullOrWhiteSpace(item))
                {
                    if (sb.Length > 0)
                        sb.Append(",");
                    sb.Append(item);
                }
            }
            return sb.ToString();
        }

        #endregion

        #region GetSqlDeleteQuery

        public SqlQuery GetSqlDeleteQuery(string tableName, IFilterItem filter, System.Collections.Generic.IEnumerable<global::Sidvall.Data.ISortItem> sortItems,
            int? rowStartIndex, int? rowCount)
        {
            return GetSqlDeleteQuery(tableName, filter, sortItems, rowStartIndex, rowCount, null);
        }
        public SqlQuery GetSqlDeleteQuery(string tableName, IFilterItem filter, System.Collections.Generic.IEnumerable<global::Sidvall.Data.ISortItem> sortItems,
            int? rowStartIndex, int? rowCount, RelationItemCollection relations)
        {
            var query = new SqlQuery(this.SqlProvider.GetParameterName(null));
            if (string.IsNullOrWhiteSpace(tableName))
                return query;
            var whereQuery = GetSqlWhereQuery(filter, null);
            var orderBySql = GetSqlOrderBy(sortItems, null);
            this.SqlProvider.InitializeSqlDelete(query, tableName, whereQuery, relations, orderBySql, rowStartIndex, rowCount);

            return query;
        }
        public SqlQuery GetSqlDeleteQuery(string tableName, System.Collections.Generic.IEnumerable<FieldItem> fields, object item, System.Type itemType)
        {
            Filter<int> filter;
            System.Reflection.PropertyInfo propertyInfo;
            object fieldValue;

            if ((fields == null) || (item == null))
            {
                filter = null;
            }
            else
            {
                if (itemType == null)
                    itemType = item.GetType();
                filter = new Filter<int>();
                foreach (var field in fields)
                {
                    if (!field.IsPrimaryKey)
                        continue;
                    propertyInfo = Sidvall.SystemContext.Current.CodeManager.GetProperty(itemType, field.FieldName);
                    if (propertyInfo == null)
                        continue;
                    fieldValue = GetPropertyValue(item, propertyInfo, field.ValueDataType);
                    filter.AddCustomFilterItem(field.FieldName, ComparisonOperator.Equal, fieldValue, field.ValueDataType);
                }
            }
            return GetSqlDeleteQuery(tableName, filter, null, null, null);
        }

        #endregion
        #region GetSqlInsertQuery

        public SqlQuery GetSqlInsertQuery(string tableName, System.Collections.Generic.IEnumerable<FieldItem> fields, object item, System.Type itemType)
        {
            var query = new SqlQuery(this.SqlProvider.GetParameterName(null));
            if (string.IsNullOrWhiteSpace(tableName))
                return query;
            if (fields == null)
                return query;
            if (item == null)
                return query;
            if (itemType == null)
                itemType = item.GetType();
            var names = string.Empty;
            var values = string.Empty;
            var index = 0;
            foreach (var field in fields)
            {
                if (field.IsGenerated)
                    continue;
                var propertyInfo = Sidvall.SystemContext.Current.CodeManager.GetProperty(itemType, field.FieldName);
                if (propertyInfo == null)
                    continue;
                if (index > 0)
                    names += ",";
                names += this.SqlProvider.GetFieldName(null, field.FieldName);
                if (index > 0)
                    values += ",";
                values += this.SqlProvider.GetParameterName(index);
                var fieldValue = GetPropertyValue(item, propertyInfo, field.ValueDataType);
                query.ParameterValues.Add(fieldValue);
                index += 1;
            }
            if (index == 0)
                return query;
            query.Append("INSERT INTO " + this.SqlProvider.GetTableName(tableName) + "(" + names + ") VALUES(" + values + ")");

            return query;
        }
        public SqlQuery GetSqlInsertQuery(string tableName, System.Collections.Generic.IEnumerable<FieldItem> fields, System.Collections.Generic.IDictionary<string, object> fieldValues)
        {
            SqlQuery query;
            object fieldValue;
            string names, values;
            int index;

            query = new SqlQuery(this.SqlProvider.GetParameterName(null));
            if (string.IsNullOrWhiteSpace(tableName))
                return query;
            if (fields == null)
                return query;
            if (fieldValues == null)
                return query;
            names = string.Empty;
            values = string.Empty;
            index = 0;
            foreach (var field in fields)
            {
                if (field.IsGenerated)
                    continue;
                if (!fieldValues.ContainsKey(field.FieldName))
                    continue;
                if (index > 0)
                    names += ",";
                names += this.SqlProvider.GetFieldName(null, field.FieldName);
                if (index > 0)
                    values += ",";
                values += this.SqlProvider.GetParameterName(index);
                fieldValue = fieldValues[field.FieldName];
                query.ParameterValues.Add(fieldValue);
                index += 1;
            }
            if (index == 0)
                return query;
            query.Append("INSERT INTO " + this.SqlProvider.GetTableName(tableName) + "(" + names + ") VALUES(" + values + ")");

            return query;
        }

        #endregion
        #region GetSqlSelectQuery

        public SqlQuery GetSqlSelectQuery(string tableName, System.Collections.Generic.IEnumerable<FieldItem> fields, IFilterItem filter,
            System.Collections.Generic.IEnumerable<global::Sidvall.Data.ISortItem> sortItems, int? rowStartIndex, int? rowCount)
        {
            return GetSqlSelectQuery(tableName, fields, filter, sortItems, rowStartIndex, rowCount, null);
        }
        public SqlQuery GetSqlSelectQuery(string tableName, System.Collections.Generic.IEnumerable<FieldItem> fields, IFilterItem filter,
            System.Collections.Generic.IEnumerable<global::Sidvall.Data.ISortItem> sortItems, int? rowStartIndex, int? rowCount, RelationItemCollection relations)
        {
            SqlQuery whereQuery;
            string fieldNames, orderBySql;
            bool hasRelations;

            var query = new SqlQuery(this.SqlProvider.GetParameterName(null));
            if ((fields == null) || (!fields.Any()))
                return query;
            fieldNames = string.Empty;
            hasRelations = (relations != null) && (relations.Count > 0);
            foreach (var field in fields)
            {
                if (!string.IsNullOrWhiteSpace(fieldNames))
                    fieldNames += ",";
                if (hasRelations)
                    fieldNames += GetFieldName(tableName, field.FieldName);
                else
                    fieldNames += GetFieldName(null, field.FieldName);
            }
            if (hasRelations)
            {
                whereQuery = GetSqlWhereQuery(filter, tableName);
                orderBySql = GetSqlOrderBy(sortItems, tableName);
            }
            else
            {
                whereQuery = GetSqlWhereQuery(filter, null);
                orderBySql = GetSqlOrderBy(sortItems, null);
            }
            this.SqlProvider.InitializeSqlSelect(query, tableName, whereQuery, relations, orderBySql, rowStartIndex, rowCount, fieldNames);

            return query;
        }

        #endregion
        #region GetSqlSelectCountQuery

        public SqlQuery GetSqlSelectCountQuery(string tableName, IFilterItem filter)
        {
            return GetSqlSelectCountQuery(tableName, filter, null);
        }
        public SqlQuery GetSqlSelectCountQuery(string tableName, IFilterItem filter, RelationItemCollection relations)
        {
            SqlQuery query, whereQuery;
            bool hasRelations;

            hasRelations = (relations != null) && (relations.Count > 0);
            query = new SqlQuery(this.SqlProvider.GetParameterName(null));
            if (hasRelations)
            {
                whereQuery = GetSqlWhereQuery(filter, tableName);
            }
            else
            {
                whereQuery = GetSqlWhereQuery(filter, null);
            }
            this.SqlProvider.InitializeSqlSelectCount(query, tableName, whereQuery, relations);

            return query;
        }

        #endregion
        #region GetSqlUpdateQuery

        public SqlQuery GetSqlUpdateQuery(string tableName, System.Collections.Generic.IEnumerable<FieldItem> fields, System.Collections.Generic.IDictionary<string, object> fieldValues)
        {
            Filter<int> filter;
            object fieldValue;

            if ((fields == null) || (fieldValues == null))
            {
                filter = null;
            }
            else
            {
                filter = new Filter<int>();
                foreach (var field in fields)
                {
                    if (!field.InUpdateFilter)
                        continue;
                    if (!fieldValues.ContainsKey(field.FieldName))
                        continue;
                    fieldValue = fieldValues[field.FieldName];
                    filter.AddCustomFilterItem(field.FieldName, ComparisonOperator.Equal, fieldValue, field.ValueDataType);
                }
            }
            return GetSqlUpdateQuery(tableName, fields, fieldValues, filter);
        }
        public SqlQuery GetSqlUpdateQuery(string tableName, System.Collections.Generic.IEnumerable<FieldItem> fields, System.Collections.Generic.IDictionary<string, object> fieldValues,
            IFilterItem filter)
        {
            object fieldValue;
            string nameValues;

            var query = new SqlQuery(this.SqlProvider.GetParameterName(null));
            if (string.IsNullOrWhiteSpace(tableName))
                return query;
            if (fields == null)
                return query;
            if (fieldValues == null)
                return query;
            nameValues = string.Empty;
            foreach (var field in fields)
            {
                if (field.IsPrimaryKey)
                    continue;
                if (field.IsGenerated)
                    continue;
                if (!fieldValues.ContainsKey(field.FieldName))
                    continue;
                if (query.ParameterValues.Count > 0)
                    nameValues += ",";
                nameValues += this.SqlProvider.GetFieldName(null, field.FieldName) + "=" + this.SqlProvider.GetParameterName(query.ParameterValues.Count);
                fieldValue = fieldValues[field.FieldName];
                query.ParameterValues.Add(fieldValue);
            }
            if (query.ParameterValues.Count == 0)
                return query;
            query.Append("UPDATE " + this.SqlProvider.GetTableName(tableName) + " SET " + nameValues);
            var whereQuery = GetSqlWhereQuery(filter, null, query.ParameterValues.Count);
            query.Append(whereQuery, " WHERE ");

            return query;
        }
        public SqlQuery GetSqlUpdateQuery(string tableName, System.Collections.Generic.IEnumerable<FieldItem> fields, object item, System.Type itemType)
        {
            Filter<int> filter;
            System.Reflection.PropertyInfo propertyInfo;
            object fieldValue;

            if ((fields == null) || (item == null))
            {
                filter = null;
            }
            else
            {
                if (itemType == null)
                    itemType = item.GetType();
                filter = new Filter<int>();
                foreach (var field in fields)
                {
                    if (!field.InUpdateFilter)
                        continue;
                    propertyInfo = Sidvall.SystemContext.Current.CodeManager.GetProperty(itemType, field.FieldName);
                    if (propertyInfo == null)
                        continue;
                    fieldValue = GetPropertyValue(item, propertyInfo, field.ValueDataType);
                    filter.AddCustomFilterItem(field.FieldName, ComparisonOperator.Equal, fieldValue, field.ValueDataType);
                }
            }
            return GetSqlUpdateQuery(tableName, fields, item, itemType, filter);
        }
        public SqlQuery GetSqlUpdateQuery(string tableName, System.Collections.Generic.IEnumerable<FieldItem> fields, object item, System.Type itemType, IFilterItem filter)
        {
            System.Reflection.PropertyInfo propertyInfo;
            object fieldValue;
            string nameValues;

            var query = new SqlQuery(this.SqlProvider.GetParameterName(null));
            if (string.IsNullOrWhiteSpace(tableName))
                return query;
            if (fields == null)
                return query;
            if (item == null)
                return query;
            if (itemType == null)
                itemType = item.GetType();
            nameValues = string.Empty;
            foreach (var field in fields)
            {
                if (field.IsPrimaryKey)
                    continue;
                if (field.IsGenerated)
                    continue;
                propertyInfo = Sidvall.SystemContext.Current.CodeManager.GetProperty(itemType, field.FieldName);
                if (propertyInfo == null)
                    continue;
                if (query.ParameterValues.Count > 0)
                    nameValues += ",";
                nameValues += this.SqlProvider.GetFieldName(null, field.FieldName) + "=" + this.SqlProvider.GetParameterName(query.ParameterValues.Count);
                fieldValue = GetPropertyValue(item, propertyInfo, field.ValueDataType);
                query.ParameterValues.Add(fieldValue);
            }
            if (query.ParameterValues.Count == 0)
                return query;
            query.Append("UPDATE " + this.SqlProvider.GetTableName(tableName) + " SET " + nameValues);
            var whereQuery = GetSqlWhereQuery(filter, null, query.ParameterValues.Count);
            query.Append(whereQuery, " WHERE ");

            return query;
        }

        #endregion

        #region ToString

        public override string ToString()
        {
            return "SqlManager - Sidvall.Data.ISqlProvider";
        }

        #endregion

        #endregion
        #region Private Members

        #region GetFieldName

        private string GetFieldName(string tableName, string fieldName)
        {
            if (!string.IsNullOrWhiteSpace(tableName))
                return this.SqlProvider.GetFieldName(tableName, fieldName);
            else
                return this.SqlProvider.GetFieldName(null, fieldName);
        }

        #endregion
        #region GetPropertyValue

        private object GetPropertyValue(object item, System.Reflection.PropertyInfo propertyInfo, DataType valueDataType)
        {
            var fieldValue = Sidvall.SystemContext.Current.CodeManager.GetPropertyValue(item, propertyInfo);
            return this.SqlProvider.GetPropertyValue(fieldValue, valueDataType);
        }

        #endregion
        #region InitializeSqlComparisonArray

        private static void InitializeSqlComparisonArray(System.Text.StringBuilder sql, string expression, string value, ComparisonOperator comparisonOperator)
        {
            if (string.IsNullOrWhiteSpace(expression))
                return;
            if (string.IsNullOrWhiteSpace(value))
                return;
            switch (comparisonOperator)
            {
                case ComparisonOperator.NotEqual:
                    sql.Append(expression + " NOT IN (" + value + ")");
                    break;
                default:
                    sql.Append(expression + " IN (" + value + ")");
                    break;
            }
        }

        #endregion
        #region InitializeSqlValueArray

        private void InitializeSqlValueArray(System.Text.StringBuilder sql, System.Array values, DataType valueDataType, bool ignoreNullValues)
        {
            if (values == null)
                return;
            var firstItem = true;
            foreach (var value in values)
            {
                var sqlValue = GetSqlValue(value, valueDataType);
                if ((ignoreNullValues) && (sqlValue.Equals("null", System.StringComparison.OrdinalIgnoreCase)))
                    continue;
                if (!firstItem)
                    sql.Append(",");
                sql.Append(sqlValue);
                firstItem = false;
            }
        }

        #endregion
        #region InitializeSqlWhereQuery

        private void InitializeSqlWhereQuery(SqlQuery query, IFilterItem filter, string tableName)
        {
            string value;

            if (filter == null)
                return;
            var subFilters = filter.GetSubFilters();
            if (subFilters == null)
            {
                var expression = GetFieldName(tableName, filter.FieldName);
                if (filter.ValueIsArray)
                {
                    value = GetSqlValueArray(filter.Value as System.Array, filter.ValueDataType, true);
                    InitializeSqlComparisonArray(query.Sql, expression, value, filter.ComparisonOperator);
                }
                else
                {
                    value = Sidvall.Serialization.SerializerManager.Current.SerializeObject(filter.Value, filter.ValueDataType);
                    if (value == null)
                    {
                        this.SqlProvider.InitializeSqlComparison(query, expression, "NULL", filter.ComparisonOperator, null);
                    }
                    else
                    {
                        this.SqlProvider.InitializeSqlComparison(query, expression, value, filter.ComparisonOperator, filter.Value);
                    }
                }
            }
            else if (subFilters.Count == 0)
            {
            }
            else if (subFilters.Count == 1)
            {
                InitializeSqlWhereQuery(query, subFilters.First(), tableName);
            }
            else
            {
                var parameterStartIndex = query.NextParameterIndex;
                var subQueries = new System.Collections.Generic.List<SqlQuery>();
                foreach (var subFilter in subFilters)
                {
                    var subQuery = GetSqlWhereQuery(subFilter, tableName, parameterStartIndex);
                    parameterStartIndex = subQuery.NextParameterIndex;
                    if (subQuery == null)
                        continue;
                    if (!subQuery.IsValid)
                        continue;
                    subQueries.Add(subQuery);
                }
                query.Append(subQueries, filter.LogicalOperator);
            }
        }

        #endregion

        #endregion
        #region Constructors

        public SqlManager(ISqlProvider sqlProvider)
        {
            this.SqlProvider = sqlProvider;
        }

        #endregion
    }
}
